#include "stdhdrs.h"
#include "rfb.h"
#include "VSocket.h"
#include "vncSockConnect.h"
#include "vncCorbaConnect.h"
#include "vncServer.h"
#include <omnithread.h>

vncServer::vncServer()
{
	m_socketConn = NULL;
	m_corbaConn = NULL;
	m_name =  NULL;

	m_autoportselect = TRUE;
	m_loopbackOnly = FALSE;
	m_loopback_allowed = TRUE;
	m_queryaccept = FALSE;
	m_beepConnect = FALSE;
	m_beepDisconnect = FALSE;
	m_enable_file_transfers = FALSE;
	m_enable_remote_inputs = TRUE;
	m_idle_timeout = 0;
	m_blacklist = 0;
	// Clear the client mapping table
 for (int x = 0; x < MAX_CLIENTS; ++x) {
    m_clientmap[x] = NULL;
 }
	m_nextid = 0;

	m_clientquitsig = new omni_condition(&m_clientsLock);
	m_port = DISPLAY_TO_PORT(0);
	m_port_http = DISPLAY_TO_HPORT(0);
}

vncServer::~vncServer()
{
   delete m_socketConn;
}

// Socket connection handling
BOOL
vncServer::SockConnect(BOOL On)
{
   // Are we being asked to switch socket connects on or off?
   if (On)
   {
      // Is there a listening socket?
      if (m_socketConn == NULL)
      {
         m_socketConn = new vncSockConnect();
         if (m_socketConn == NULL)
            return FALSE;

         // Are we to use automatic port selection?
         if (m_autoportselect)
         {
            BOOL ok = FALSE;

            // Yes, so cycle through the ports, looking for a free one!
            for (int i = 0; i < 99; i++)
            {
               m_port = DISPLAY_TO_PORT(i);
               m_port_http = DISPLAY_TO_HPORT(i);

               vnclog.Print(LL_CLIENTS, VNCLOG("trying port number %d\n"), m_port);

               // Attempt to connect to the port
               VSocket tempsock;
               if (tempsock.Create())
               {
                  if (!tempsock.Connect("localhost", m_port))
                  {
                     // Couldn't connect, so this port is probably usable!
                     if (m_socketConn->Init(this, m_port))
                     {
                        ok = TRUE;
                        break;
                     }
                  }
               }
            }

            if (!ok)
            {
               delete m_socketConn;
               m_socketConn = NULL;
               return FALSE;
            }
         } else
         {
            // No autoportselect
            if (!m_socketConn->Init(this, m_port))
            {
               delete m_socketConn;
               m_socketConn = NULL;
               return FALSE;
            }
         }

         // Now let's start the HTTP connection stuff
//--          if (m_port_http == m_port) {
//             vnclog.Print(LL_INTERR, VNCLOG("cannot start both RFB and HTTP servers "
//                "on the same port\n"));
//          }
//          if (m_httpConn == NULL && m_httpd_enabled && m_port_http != m_port) {
//             m_httpConn = new vncHTTPConnect;
//             if (m_httpConn != NULL) {
//                // Start up the HTTP server
//                if (!m_httpConn->Init(this, m_port_http,
//                   m_httpd_params_enabled)) {
//                      delete m_httpConn;
//                      m_httpConn = NULL;
//                      return FALSE;
//                }
//             }
//--          }
      }
   }
   else
   {
      // *** JNW - Trying to fix up a lock-up when the listening socket closes
#ifndef HORIZONLIVE
//--       KillAuthClients();
//       KillUnauthClients();
//       WaitUntilAuthEmpty();
//--       WaitUntilUnauthEmpty();
#endif

      // Is there a listening socket?
      if (m_socketConn != NULL)
      {
         // Close the socket
         delete m_socketConn;
         m_socketConn = NULL;
      }

      // Is there an HTTP socket active?
//-      if (m_httpConn != NULL)
//       {
//          // Close the socket
//          delete m_httpConn;
//          m_httpConn = NULL;
//-      }
   }

   return TRUE;
}

BOOL
vncServer::SockConnected()
{
   return NULL != m_socketConn;
}

BOOL
vncServer::LoopbackOnly()
{
	return m_loopbackOnly;
}

// Name and port number handling
void
vncServer::SetName(const char * name)
{
	// Set the name of the desktop
	if (m_name != NULL)
	{
		free(m_name);
		m_name = NULL;
	}

	m_name = strdup(name);
}

// RemoveClient should ONLY EVER be used by the client to remove itself.
void
vncServer::RemoveClient(vncClientId clientid)
{
	vncClientList::iterator i;
	BOOL done = FALSE;

	omni_mutex_lock l1(m_desktopLock);
	{	omni_mutex_lock l2(m_clientsLock);

	// Find the client in one of the two lists
	for (i = m_unauthClients.begin(); i != m_unauthClients.end(); i++)
	{
		// Is this the right client?
		if ((*i) == clientid)
		{
			vnclog.Print(LL_INTINFO, VNCLOG("removing unauthorized client\n"));

			// Yes, so remove the client and kill it
			m_unauthClients.erase(i);
			m_clientmap[clientid] = NULL;

			done = TRUE;
			break;
		}
	}
	if (!done)
	{
		for (i = m_authClients.begin(); i != m_authClients.end(); i++)
		{
			// Is this the right client?
			if ((*i) == clientid)
			{
				vnclog.Print(LL_INTINFO, VNCLOG("removing authorized client\n"));

				// Yes, so remove the client and kill it
				m_authClients.erase(i);
				m_clientmap[clientid] = NULL;

				done = TRUE;
				break;
			}
		}
	}

	// Signal that a client has quit
	m_clientquitsig->signal();

	} // Unlock the clientLock

	// If so configured, beep to indicate the old connection is
	// gone.
	if (GetBeepDisconnect())
	{
		MessageBeep(MB_OK);
	}

	// Are there any authorized clients connected?
//-- 	if (m_authClients.empty() && (m_desktop != NULL))
// 	{
// 		vnclog.Print(LL_STATE, VNCLOG("deleting desktop server\n"));
// 
// 		// Are there locksettings set?
// 		if (LockSettings() == 1)
// 		{
// 			// Yes - lock the machine on disconnect!
// 			vncService::LockWorkstation();
// 		} else if (LockSettings() > 1)
// 		{
// 			char username[UNLEN+1];
// 
// 			vncService::CurrentUser((char *)&username, sizeof(username));
// 			if (strcmp(username, "") != 0)
// 			{
// 				// Yes - force a user logoff on disconnect!
// 				if (!ExitWindowsEx(EWX_LOGOFF, 0))
// 					vnclog.Print(LL_CONNERR, VNCLOG("client disconnect - failed to logoff user!\n"));
// 			}
// 		}
// 
// 		// Delete the screen server
// 		delete m_desktop;
// 		m_desktop = NULL;
//-- 	}

	// Notify anyone interested of the change
	//--DoNotify(WM_SRV_CLIENT_DISCONNECT, 0, 0);

	vnclog.Print(LL_INTINFO, VNCLOG("RemoveClient() done\n"));
}

void
vncServer::AddAuthHostsBlacklist(const char *machine) {
	omni_mutex_lock l(m_clientsLock);

	// -=- Is the specified host blacklisted?
	vncServer::BlacklistEntry	*current = m_blacklist;

	// Get the current time as a 64-bit value
	SYSTEMTIME					systime;
	FILETIME					ftime;
	LARGE_INTEGER				now;
	GetSystemTime(&systime);
	SystemTimeToFileTime(&systime, &ftime);
	now.LowPart=ftime.dwLowDateTime;
 now.HighPart=ftime.dwHighDateTime;
	now.QuadPart /= 10000000; // Convert it into seconds

	while (current) {

		// Is this the entry we're interested in?
		if (strcmp(current->_machineName, machine) == 0) {

			// If the host is already blocked then ignore
			if (current->_blocked)
				return;

			// Set the RefTime & failureCount
			current->_lastRefTime.QuadPart = now.QuadPart + 10;
			current->_failureCount++;

			if (current->_failureCount > 5)
				current->_blocked = TRUE;
			return;
		}

		current = current->_next;
	}

	// Didn't find the entry
	current = new vncServer::BlacklistEntry;
	current->_blocked = FALSE;
	current->_failureCount = 0;
	current->_lastRefTime.QuadPart = now.QuadPart + 10;
	current->_machineName = strdup(machine);
	current->_next = m_blacklist;
	m_blacklist = current;
}

void
vncServer::RemAuthHostsBlacklist(const char *machine) {
	omni_mutex_lock l(m_clientsLock);

	// -=- Is the specified host blacklisted?
	vncServer::BlacklistEntry	*current = m_blacklist;
	vncServer::BlacklistEntry	*previous = 0;

	while (current) {

		// Is this the entry we're interested in?
		if (strcmp(current->_machineName, machine) == 0) {
			if (previous)
				previous->_next = current->_next;
			else
				m_blacklist = current->_next;
			vncServer::BlacklistEntry *next = current->_next;
			free (current->_machineName);
			delete current;
			current = next;
			continue;
		}

		previous = current;
		current = current->_next;
	}
}
vncClientId
vncServer::AddClient(VSocket *socket, BOOL reverse, BOOL shared)
{
	return AddClient(socket, reverse, shared, TRUE, TRUE);
}

vncClientId
vncServer::AddClient(VSocket *socket, BOOL reverse, BOOL shared,
					 BOOL keysenabled, BOOL ptrenabled)
{
	vncClient * client = NULL;

	omni_mutex_lock l(m_clientsLock);

	// Try to allocate a client id...
	vncClientId clientid = m_nextid;
	do
	{
		clientid = (clientid+1) % MAX_CLIENTS;
		if (clientid == m_nextid)
		{
			vnclog.Print(LL_CONNERR, VNCLOG("too many clients already connected\n"));
			delete socket;
			return -1;
		}
	}
	while (m_clientmap[clientid] != NULL);

	// Create a new client and add it to the relevant client list
	client = new vncClient();
	if (client == NULL) {
		vnclog.Print(LL_CONNERR, VNCLOG("failed to allocate client object\n"));
		delete socket;
		return -1;
	}

	// Set the client's settings
// 	client->EnableKeyboard(keysenabled && m_enable_remote_inputs);
// 	client->EnablePointer(ptrenabled && m_enable_remote_inputs);

	// Start the client
	if (!client->Init(this, socket, reverse, shared, clientid))
	{
		// The client will delete the socket for us...
		vnclog.Print(LL_CONNERR, VNCLOG("failed to initialize client object\n"));
		delete client;
		return -1;
	}

	m_clientmap[clientid] = client;

	// Add the client to unauth the client list
	m_unauthClients.push_back(clientid);

	// Notify anyone interested about this event
	//--DoNotify(WM_SRV_CLIENT_CONNECT, 0, 0);

	vnclog.Print(LL_INTINFO, VNCLOG("AddClient() done\n"));

	return clientid;
}

// Client info retrieval/setup
vncClient*
vncServer::GetClient(vncClientId clientid)
{
	if ((clientid >= 0) && (clientid < MAX_CLIENTS))
		return m_clientmap[clientid];
	return NULL;
}